﻿package moodish.security;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.Queue;

import moodish.comm.ServerComm;
import moodish.comm.ServerSide;
import moodish.comm.ServerSideMessage;
import moodish.comm.ServerSideMessage.Type;
import moodish.dummy.ServerCommDummy;

/**
 * Esta classe serÃ¡ usada como filtro de seguranÃ§a para a comunicaÃ§Ã£o entre
 * Cliente e Servidor. UsarÃ¡ diversos HashMaps para guardar certas informaÃ§Ãµes
 * dos clientes que sÃ£o o tempo de desconeÃ§Ã£o, tempo entre mensagens e nÃºmero de
 * mensagens enviadas.
 *
 * @author David Vilhena,Patricia Coimbra,Marilia Dionisio
 * @version 1
 */
public class SecurityFilter implements ServerComm {

    private ServerCommDummy server;
    private HashMap<String, Long> mapaTempos = new HashMap<String, Long>();
    private HashMap<String, Integer> mapaMsg = new HashMap<String, Integer>();
    private HashMap<String, Long> tmpDisconnect = new HashMap<String, Long>();
    private long time = 0;

    public SecurityFilter(ServerCommDummy server2) {
        super();
        this.server = server2;
    }

    @Override
    public void start() {
        server.start();
    }

    /**
     * Esta classe limita o nÃºmero de mudanÃ§as de estado que o cliente pode
     * fazer num certo tempo. Se houver de demasiadas mensagens enviadas dentro
     * de um certo limite de tempo o cliente pode ser desconectado atravÃ©s de
     * server.disconnectClient(nomeCliente) ou recebe um aviso
     * server.sendError(nomeCliente, "Aviso")
     *
     * @return server.getNextMessage() quando acaba de fazer as verificaÃ§Ãµes diz
     * ao servidor para ler a prÃ³xima mensagem
     *
     */
    @Override
    public ServerSideMessage getNextMessage() {
        ServerSideMessage mensagem = null;
        if(server.hasNextMessage()){
            mensagem=server.getListmessage().poll();
            //        server.getNextMessage();
            time=System.currentTimeMillis();
            Type typeMsg = mensagem.getType();
            String nomeCliente = mensagem.getClientNickname();
            
            if (typeMsg.equals(ServerSideMessage.Type.MOODISH_MESSAGE)) {
                if (mapaMsg.containsKey(nomeCliente) && mapaTempos.containsKey(nomeCliente)) {
                    long timeMsg = mapaTempos.get(nomeCliente);
                    long timeDif=time-timeMsg;
                    if(timeDif>60000)
                    {
                        mapaTempos.remove(nomeCliente);
                        mapaTempos.put(nomeCliente, time);
                        mapaMsg.remove(nomeCliente);
                        mapaMsg.put(nomeCliente, 1);
                    }
                    int nMsg = mapaMsg.get(nomeCliente);
                    mapaMsg.remove(nomeCliente);
                    mapaMsg.put(nomeCliente, (nMsg+1));
                    if (timeDif < 30000) {
                        if (mapaMsg.get(nomeCliente) > 10) {
                            //    server.AddMessage(new ServerSide(nomeCliente, "", ServerSideMessage.Type.CLIENT_DISCONNECTED));
                            server.disconnectClient(nomeCliente);
                            mapaMsg.remove(nomeCliente);
                            mapaTempos.remove(nomeCliente);
                            tmpDisconnect.put(nomeCliente, time);
                            //return (new ServerSide(nomeCliente, "", ServerSideMessage.Type.CLIENT_DISCONNECTED));
                        } else if (mapaMsg.get(nomeCliente) > 8) {
                            server.sendError(nomeCliente, "Erro, mudaste mais de 8 vezes em menos de 30 segundos!");
                            //    server.AddMessage(new ServerSide(nomeCliente, "Erro, mudaste mais de 8 vezes em menos de 30 segundos!", ServerSideMessage.Type.ERROR));
                        }
                    } 
                    if (timeDif < 60000) {
                        if (mapaMsg.get(nomeCliente) > 3) {
                            //server.AddMessage(new ServerSide(nomeCliente, "", ServerSideMessage.Type.CLIENT_DISCONNECTED));

                            mapaMsg.remove(nomeCliente);
                            mapaTempos.remove(nomeCliente);
                            tmpDisconnect.put(nomeCliente, time);
                            server.disconnectClient(nomeCliente);
                            //return (new ServerSide(nomeCliente, "", ServerSideMessage.Type.CLIENT_DISCONNECTED));
                        } else if (mapaMsg.get(nomeCliente) == 2) {
                            server.sendError(nomeCliente, "Erro, mudaste 2 vezes em menos de 60 segundos!");
                            //                                server.AddMessage(new ServerSide(nomeCliente, "Erro, mudaste 2 vezes em menos de 60 segundos!", ServerSideMessage.Type.ERROR));
                        }

                    }
                }
                else {
                    mapaMsg.put(nomeCliente, 1);
                    mapaTempos.put(nomeCliente, time);
                    return mensagem;
                }
            }
        }
        return mensagem;
    }

    @Override
    public boolean hasNextMessage() {
        return server.hasNextMessage();
    }

    @Override
    public void sendNewFriendship(String toNickname, String newFriendship) {
        server.sendNewFriendship(toNickname, newFriendship);
    }

    @Override
    public void sendNewUnfriendship(String toNickname, String unfriendship) {
        server.sendNewUnfriendship(toNickname, unfriendship);
    }

    @Override
    public void sendError(String toNickname, String error) {
        server.sendError(toNickname, error);
    }

    /**
     * Esta classe impossibilita o cliente de se conectar novamente caso tenha
     * sido banido do servidor atravÃ©s do mÃ©todo usado acima getNextMessage() Se
     * o cliente tentar conectar-se antes que tenha passado 30 segundos, nÃ£o
     * serÃ¡ feita a conecÃ§Ã£o, recebendo um aviso
     * server.sendError(toNickname,"Aviso"); Caso jÃ¡ tenha passado 30 segundos
     * ou o utilizador nunca foi banido, o mÃ©todo encaminha o utilizador para a
     * conecÃ§Ã£o server.sendClientConnected(toNickname, connectedNickname);
     *
     */
    @Override
    public void sendClientConnected(String toNickname, String connectedNickname) {
        if (tmpDisconnect.containsKey(connectedNickname)) {
            if (time - tmpDisconnect.get(connectedNickname) < 30000) 
                server.sendError(toNickname, "Tens de esperar 30 segundos para que te possas conectar novamente.");
            else {
                tmpDisconnect.remove(connectedNickname);

                server.sendClientConnected(toNickname, connectedNickname);
            }
        }
        server.sendClientConnected(toNickname, connectedNickname);
    }

    @Override
    public void sendClientDisconnected(String toNickname,
            String disconnectedNickname) {
        server.sendClientDisconnected(toNickname, disconnectedNickname);
    }

    @Override
    public boolean clientIsConnected(String nickname) {
        return server.clientIsConnected(nickname);
    }

    @Override
    public void disconnectClient(String nickname) {
        server.disconnectClient(nickname);
    }

    @Override
    public void sendMoodishMessage(String fromNicename, String toNickname,
            String moodishMessage) {
        server.sendMoodishMessage(fromNicename, toNickname, moodishMessage);
    }

}

